package com.qire.antscompiler.generator;

import com.qire.antscompiler.utils.Log;
import com.qire.antscore.annotation.InterceptorMapping;
import com.qire.antscore.common.AssertUtils;
import com.qire.antscore.constant.GeneratorConfig;
import com.qire.antscore.entity.RulesMeta;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;

/**
 * 拦截规则加载器代码生成器
 */
public class InterceptorCodeGenerator {

    private Log log;

    // 拦截器规则接口类全名拦截器规则接口全面，这里如果有改动一定小心确保一致
    private final String INTERCEPT_RULES = "com.qire.antsrouter.intercept.InterceptRules";

    // 节点工具类 (类、函数、属性都是节点)
    private Elements elementUtils;

    // 文件生成器 类/资源
    private Filer filerUtils;

    // 模块名
    private final String moduleName;

    public InterceptorCodeGenerator(ProcessingEnvironment processingEnv, String moduleName) {
        this.log = Log.newLog(processingEnv.getMessager());
        this.elementUtils = processingEnv.getElementUtils();
        this.filerUtils = processingEnv.getFiler();
        this.moduleName = moduleName;
    }

    /**
     * 解析拦截器
     */
    public void parseInterceptor(Set<? extends Element> elements) throws IOException {

        if(AssertUtils.isEmpty(elements)) {
            log.i("InterceptorCodeGenerator：        elements 为空没有需要处理的");
            return;
        }
        log.i("InterceptorCodeGenerator：        开始处理了！");

        // 获得拦截器规则加载器接口类型
        TypeElement iInterceptorGroup = elementUtils.getTypeElement(GeneratorConfig.I_INTERCEPTOR_LOADER);
        // 获得拦截器规则接口类型
        TypeElement interceptorTypeElement = elementUtils.getTypeElement(INTERCEPT_RULES);

        if(AssertUtils.isNull(interceptorTypeElement)) {
            throw new RuntimeException("INTERCEPT_RULES 实际路径不存在,常量 INTERCEPT_RULES 是否与 antsRouter 模块中的接口全路径一致: " + INTERCEPT_RULES);
        }

        TypeMirror interceptorTypeMirror = interceptorTypeElement.asType();

        // 拦截规则元数据代码节点集合，自排序
        TreeSet<RulesMetaCodeNode> codeNodes = new TreeSet<>((node1, node2)-> node1.compareTo(node2));

        for (Element element : elements) {
            // 验证节点是否含有拦截器注解
            InterceptorMapping interceptorMapping = element.getAnnotation(InterceptorMapping.class);
            List<? extends TypeMirror> typeMirrors = ((TypeElement) element).getInterfaces();
            if(interceptorMapping == null) {
                continue;
            }
            if(!typeMirrors.contains(interceptorTypeMirror)) {
                continue;
            }

            codeNodes.add(new RulesMetaCodeNode(interceptorMapping, (TypeElement)element));
        }

        log.i("InterceptorCodeGenerator：        RulesMetaCodeNode 整理排序完毕");

        // 构建一个Map<Integer, RulesMeta>类型
        ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(Integer.class),
                ClassName.get(RulesMeta.class));

        // 申明一个变量 Map<Integer, RulesMeta> interceptors
        ParameterSpec parameterSpec = ParameterSpec.builder(parameterizedTypeName, "interceptors").build();

        // 定义一个方法
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(GeneratorConfig.METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(parameterSpec);

        // 为方法 public void loadInto(Map<Integer, RulesMeta> interceptors){} 添加函数体
        if(AssertUtils.notEmpty(codeNodes)) {
            int codeIndex = 0;
            // 为每一个添加注解映射的类生成一条对应注册语句，interceptors.put(0,RulesMeta.build(new String[]{""},TestIn1.class));
            for (RulesMetaCodeNode node : codeNodes) {
//                InterceptorMapping interceptorMapping = entry.getValue().getAnnotation(InterceptorMapping.class);
//                methodBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)",ClassName.get((TypeElement) entry.getValue()));

                // 函数体的添加
                node.addStatement(codeIndex++, methodBuilder);
            }
        }

        // 构建一个 public class AntsRouter_Interceptor_moduleName implements IInterceptorLoader{} 类
        TypeSpec AntsRouter_Interceptor_Class = TypeSpec.classBuilder(GeneratorConfig.CLASS_INTERCEPTOR_PREFIX + moduleName)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(methodBuilder.build())
                .addSuperinterface(ClassName.get(iInterceptorGroup))
                .build();

        //将AntsRouter_Interceptor文件写入磁盘中,路径是在app/build/source/api/debug/PACKAGE_OF_GENERATE_FILE下面
        JavaFile.builder(GeneratorConfig.PACKAGE_OF_GENERATE_FILE, AntsRouter_Interceptor_Class).build().writeTo(filerUtils);

    }

    private class RulesMetaCodeNode {
        private final InterceptorMapping interceptorMapping;
        private final TypeElement typeElement;

        private RulesMetaCodeNode(InterceptorMapping interceptorMapping, TypeElement typeElement) {
            this.interceptorMapping = interceptorMapping;
            this.typeElement = typeElement;
        }

        private void addStatement(int codeIndex, MethodSpec.Builder methodBuilder) {
            // 函数体的添加
            methodBuilder.addStatement("interceptors.put($L,$T.build(new String[]{\"$L\"},$T.class))",
                    codeIndex,
                    ClassName.get(RulesMeta.class),
                    String.join("\",\"", interceptorMapping.group()),
                    ClassName.get(typeElement));
        }

        private int compareTo(RulesMetaCodeNode node) {
            int compare = interceptorMapping.priority() - node.interceptorMapping.priority();
            return compare == 0 ? 1 : compare;
        }

    }

}
